package excel;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import util.JsonUtil;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * excel工具类
 *
 * @author zhanglinfeng
 * @date create in 2023/6/5 10:20
 */
public class ExcelUtil {

    /**
     * 导出.xls格式的excel
     *
     * @param title    标题
     * @param cls      class
     * @param dataList 数据
     */
    public static <T> Workbook exportXlsExcel(String title, Class<T> cls, List<T> dataList) throws Exception {
        return exportExcel(new HSSFWorkbook(), title, cls, dataList);
    }

    /**
     * 导出.xlsx格式的excel
     *
     * @param title    标题
     * @param cls      class
     * @param dataList 数据
     */
    public static <T> Workbook exportXlsxExcel(String title, Class<T> cls, List<T> dataList) throws Exception {
        return exportExcel(new XSSFWorkbook(), title, cls, dataList);
    }

    /**
     * 导出大数据的excel
     *
     * @param title    标题
     * @param cls      class
     * @param dataList 数据
     */
    public static <T> Workbook exportBigExcel(String title, Class<T> cls, List<T> dataList) throws Exception {
        return exportExcel(new SXSSFWorkbook(100), title, cls, dataList);
    }

    private static <T> Workbook exportExcel(Workbook workbook, String title, Class<T> cls, List<T> dataList) throws Exception {
        List<ExcelHeaderVO> headerList = getHeaderList(cls);
        try (workbook) {
            Sheet sheet = workbook.createSheet();
            int rowNum = 0;
            //插入标题数据
            Row titleRow = sheet.createRow(rowNum++);
            ExcelStyleUtil.createCell(titleRow, 0, title, ExcelStyleUtil.titleCellStyle(workbook));
            ExcelStyleUtil.addMergedRegion(sheet, 0, 0, 0, headerList.size() - 1);
            //插入表头数据
            Row headerRow = sheet.createRow(rowNum++);
            CellStyle headerCellStyle = ExcelStyleUtil.headerCellStyle(workbook);
            IntStream.range(0, headerList.size()).forEach(i -> ExcelStyleUtil.createCell(headerRow, i, headerList.get(i).getName(), headerCellStyle));
            //插入主体数据
            CellStyle commonStyle = ExcelStyleUtil.contentCellStyle(workbook);
            CellStyle numberStyle = ExcelStyleUtil.numberCellStyle(workbook);
            for (Object obj : dataList) {
                Row row = sheet.createRow(rowNum++);
                Map<String, Object> objMap = JsonUtil.toObjMap(obj);
                for (int j = 0; j < headerList.size(); j++) {
                    ExcelHeaderVO header = headerList.get(j);
                    Object objVal = objMap.get(header.getColumnName());
                    if (objVal instanceof Number){
                        ExcelStyleUtil.createCell(row, j, header.getColumnValue(objVal), numberStyle);
                    }else {
                        ExcelStyleUtil.createCell(row, j, header.getColumnValue(objVal), commonStyle);
                    }
                }
                //自适应高度
                row.setHeight((short) -1);
            }
            //自适应宽度
            for (int i = 0; i < headerList.size(); i++) {
                sheet.autoSizeColumn(i);
            }
        } catch (Exception e) {
            throw new Exception("导出" + title + "失败", e);
        }
        return workbook;
    }

    private static <T> List<ExcelHeaderVO> getHeaderList(Class<T> cls) {
        Map<Integer, ExcelHeaderVO> map = new HashMap<>(2);
        for (Field field : cls.getDeclaredFields()) {
            Optional.ofNullable(field.getAnnotation(ExcelHeader.class)).ifPresent(t -> map.put(t.order(), new ExcelHeaderVO(t.name(), field.getName(), t.format())));
        }
        return map.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList());
    }

    /**
     * 不生成文件，直接返回流给前端
     *
     * @param response 请求响应
     * @param workbook Workbook
     * @param fileName 带后缀的文件名
     */
    public static void writeResponseStream(HttpServletResponse response, Workbook workbook, String fileName) throws IOException {
        response.setContentType("application/octet-stream");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
        OutputStream outputStream = response.getOutputStream();
        workbook.write(outputStream);
        workbook.close();
        outputStream.flush();
        outputStream.close();
    }
}
